﻿/*
* The shader code below is licensed under the Creative Commons Attribution-Share Alike 3.0 
* license.
*
* You are free:
*	To Share: to copy, distribute, display, and perform the work
*	To Remix: to make derivative works 
*
*	Under the following conditions:
*		* Attribution - You must attribute the work in the manner specified by the author or
*			licensor (but not in any way that suggests that they endorse you or your use of the work).
*		* Share Alike - If you alter, transform, or build upon this work, you may distribute
*			the resulting work only under the same, similar or a compatible license.
*
*		For any reuse or distribution, you must make clear to others the license terms of this work.
*		The best way to do this is with a link to the above web page (http;//www.tesla-engine.net)
*
*		Any of the above conditions can be waived if you get permission from the copyright holder.
*
*		Apart from the remix rights granted under this license, nothing in this license impairs or
*		restricts the author's moral rights.
*/

#include "Structures.fxh"
#include "Lighting.fxh"

//World params
uniform float4x4 WVP;
uniform float4x4 World;
uniform float3 EyePos;

//Light params
uniform float3 GlobalAmbient : GLOBALAMBIENT = { 0.0f, 0.0f, 0.0f };
uniform Light Lights[4] : LIGHTLIST;
uniform int NumLights : LIGHTCOUNT = 0;

//Material params
uniform float3 MatDiffuse = { 1.0f, 1.0f, 1.0f};
uniform float3 MatAmbient = { 0.2f, 0.2f, 0.2f};
uniform float3 MatEmissive = { 0.0f, 0.0f, 0.0f};
uniform float3 MatSpecular = { 0.0f, 0.0f, 0.0f};
uniform float MatShininess = 16.0f;
uniform float Alpha = 1.0f;

//Normal map params
uniform bool UseDiffuseMap = true;
uniform bool UseSpecularMap = false;


uniform texture2D DiffuseMap;
uniform sampler2D DiffuseMapSampler =
sampler_state {
  Texture = <DiffuseMap>;
  MipFilter = LINEAR;
  MinFilter = LINEAR;
  MagFilter = LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

uniform texture2D NormalMap;
uniform sampler2D NormalMapSampler =
sampler_state {
  Texture = <NormalMap>;
  MipFilter = LINEAR;
  MinFilter = LINEAR;
  MagFilter = LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

uniform texture2D SpecularMap;
uniform sampler2D SpecularMapSampler =
sampler_state {
  Texture = <SpecularMap>;
  MipFilter = LINEAR;
  MinFilter = LINEAR;
  MagFilter = LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

struct VSOutputNmBnTnTx {
	float4 PositionPS : SV_POSITION;
	float3 PositionWS : TEXCOORD0;
	float2 TexCoord : TEXCOORD1;
	float3 NormalWS : TEXCOORD2;
	float3 TangentWS : TEXCOORD3;
	float3 BinormalWS : TEXCOORD4;
};

struct VSOutputNmBnTnTxVc {
	float4 PositionPS : SV_POSITION;
	float3 PositionWS : TEXCOORD0;
	float2 TexCoord : TEXCOORD1;
	float3 NormalWS : TEXCOORD2;
	float3 TangentWS : TEXCOORD3;
	float3 BinormalWS : TEXCOORD4;
	float4 VertColor : COLOR;
};

//
// **** NormalMapTexture ****
//

VSOutputNmBnTnTx VS_NormalMapTx(VSInputNmTxBT input) {
	VSOutputNmBnTnTx output;
	
	float4 pos = float4(input.Position, 1.0f);

	output.PositionPS = mul(pos, WVP);
	output.PositionWS = mul(pos, World).xyz;
	output.TexCoord = input.TexCoord;

	//Transform BNT to world space
	output.NormalWS = normalize(mul(float4(input.Normal, 0.0f), World).xyz);
	output.TangentWS = normalize(mul(float4(input.Tangent, 0.0f), World).xyz);
	output.BinormalWS = normalize(mul(float4(input.Binormal, 0.0f), World).xyz);
	return output;
}

float4 PS_NormalMapTx(VSOutputNmBnTnTx input) : SV_TARGET {
	//Get the normal
	float4 texNormal = Expand(tex2D(NormalMapSampler, input.TexCoord));

	//Transform it to world space
	texNormal = float4((input.NormalWS * texNormal.z) + (texNormal.x * input.TangentWS + texNormal.y * -input.BinormalWS), 0.0f);

	float3 normalWS = normalize(texNormal.xyz);

	//Get the diffuse map color
	float4 albedo = float4(1.0f, 1.0f, 1.0f, 1.0f);
	if(UseDiffuseMap) {
		albedo = tex2D(DiffuseMapSampler, input.TexCoord);
	}

	//Get the specular map color
	float4 spec = float4(1.0f, 1.0f, 1.0f, 1.0f);
	if(UseSpecularMap) {
		spec = tex2D(SpecularMapSampler, input.TexCoord);
	}

	//Calc the view V vector for specular highlights
	float3 V = normalize(EyePos - input.PositionWS);
	float3 lightContribs = float3(0.0f, 0.0f, 0.0f);

	Material mat;
	mat.Ambient = MatAmbient;
	mat.Diffuse = MatDiffuse;
	mat.Specular = MatSpecular;
	mat.Shininess = MatShininess;

	for(int i = 0; i < NumLights; i++) {
		LightResult result = ComputeSingleLight(Lights[i], input.PositionWS, normalWS, V, mat);
		float3 ambient = result.Ambient;
		float3 diffuse = result.Diffuse;
		float3 specular = result.Specular;

		diffuse = (diffuse + ambient) * albedo.rgb;

		specular *= spec.xyz;

		lightContribs += ComputeAttenuation(input.PositionWS, Lights[i]) * SpotEffect(input.PositionWS, Lights[i]) * (diffuse + specular);
	}

	float3 color = (MatAmbient * GlobalAmbient) + MatEmissive + lightContribs;

	return float4(color, Alpha * albedo.a);
}

technique NormalMapTexture {
	pass Pass0 {
    VertexShader = compile vs_3_0 VS_NormalMapTx();
    PixelShader = compile ps_3_0 PS_NormalMapTx();
	}
}

//
// **** NormalMapVertColorTexture ****
//

VSOutputNmBnTnTxVc VS_NormalMapTxVc(VSInputNmVcTxBT input) {
	VSOutputNmBnTnTxVc output;
	
	float4 pos = float4(input.Position, 1.0f);

	output.PositionPS = mul(pos, WVP);
	output.PositionWS = mul(pos, World).xyz;
	output.TexCoord = input.TexCoord;
	output.VertColor = input.VertColor;

	//Transform BNT to world space
	output.NormalWS = normalize(mul(float4(input.Normal, 0.0f), World).xyz);
	output.TangentWS = normalize(mul(float4(input.Tangent, 0.0f), World).xyz);
	output.BinormalWS = normalize(mul(float4(input.Binormal, 0.0f), World).xyz);
	return output;
}

float4 PS_NormalMapTxVc(VSOutputNmBnTnTxVc input) : SV_TARGET {
	//Get the normal
	float4 texNormal = Expand(tex2D(NormalMapSampler, input.TexCoord));

	//Transform it to world space
	texNormal = float4((input.NormalWS * texNormal.z) + (texNormal.x * input.TangentWS + texNormal.y * -input.BinormalWS), 0.0f);

	float3 normalWS = normalize(texNormal.xyz);

	//Get the diffuse map color
	float4 albedo = float4(1.0f, 1.0f, 1.0f, 1.0f);
	if(UseDiffuseMap) {
		albedo = tex2D(DiffuseMapSampler, input.TexCoord);
	}

	//Get the specular map color
	float4 spec = float4(1.0f, 1.0f, 1.0f, 1.0f);
	if(UseSpecularMap) {
		albedo = tex2D(SpecularMapSampler, input.TexCoord);
	}

	//Calc the view V vector for specular highlights
	float3 V = normalize(EyePos - input.PositionWS);
	float3 lightContribs = float3(0.0f, 0.0f, 0.0f);

	Material mat;
	mat.Ambient = MatAmbient;
	mat.Diffuse = MatDiffuse;
	mat.Specular = MatSpecular;
	mat.Shininess = MatShininess;

	for(int i = 0; i < NumLights; i++) {
		LightResult result = ComputeSingleLight(Lights[i], input.PositionWS, normalWS, V, mat);
		float3 ambient = result.Ambient;
		float3 diffuse = result.Diffuse;
		float3 specular = result.Specular;

		diffuse = (diffuse + ambient) * albedo.rgb * input.VertColor.rgb;

		specular *= spec.xyz;

		lightContribs += ComputeAttenuation(input.PositionWS, Lights[i]) * SpotEffect(input.PositionWS, Lights[i]) * (diffuse + specular);
	}

	float3 color = (MatAmbient * GlobalAmbient) + MatEmissive + lightContribs;

	return float4(color, Alpha * albedo.a * input.VertColor.a);
}

technique NormalMapVertColorTexture {
	pass Pass0 {
    VertexShader = compile vs_3_0 VS_NormalMapTxVc();
    PixelShader = compile ps_3_0 PS_NormalMapTxVc();
	}
}